package com.example.component;

import cn.dev33.satoken.stp.StpInterface;
import com.example.domain.Resource;
import com.example.domain.Role;
import com.example.domain.RoleResource;
import com.example.domain.UserRole;
import com.example.service.ResourceService;
import com.example.service.RoleResourceService;
import com.example.service.RoleService;
import com.example.service.UserRoleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@Slf4j
@Component
public class StpInterfaceImpl implements StpInterface {
    @Autowired
    private UserRoleService userRoleService;
    @Autowired
    private RoleService roleService;
    @Autowired
    private RoleResourceService roleResourceService;
    @Autowired
    private ResourceService resourceService;

    private static final String permissionCachedKey = "permission:{loginId}";
    private static final String roleCachedKey = "role:{loginId}";

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 返回一个账号的权限码集合（资源码）
     * 此处可以优化：第一次查询时读DB，然后缓存到Redis，后续查询走Redis，这样可以避免频繁查询DB
     * 当修改角色的权限时，需要清空Redis缓存
     */
    @Override
    public List<String> getPermissionList(Object loginId, String loginType) {
        // 读缓存
        List<String> permissionList = getPermissionListFromRedis(loginId);
        if (permissionList != null) {
            log.info("redis => loginId: {}, permissionList: {}", loginId, permissionList);
            return permissionList;
        }
        permissionList = new ArrayList<>();
        List<Role> userRoles = getUserRoles(loginId);
        if (userRoles.isEmpty()) {
            return Collections.emptyList();
        }
        for (Role role : userRoles) {
            List<RoleResource> roleResources = roleResourceService.lambdaQuery().eq(RoleResource::getRoleId, role.getId()).list();
            for (RoleResource roleResource : roleResources) {
                Resource curResource = resourceService.getById(roleResource.getResourceId());
                if (curResource != null) {
                    permissionList.add(curResource.getPath());
                }
            }
        }
        // 写缓存
        setPermissionListToRedis(loginId, permissionList);
        log.info("db => loginId: {}, permissionList: {}", loginId, permissionList);
        return permissionList;
    }


    /**
     * 返回一个用户的角色集合
     */
    @Override
    public List<String> getRoleList(Object loginId, String loginType) {
        // 读缓存
        List<String> roles = getRoleListFromRedis(loginId);
        if (roles != null) {
            log.info("redis => loginId: {}, roles: {}", loginId, roles);
            return roles;
        }
        List<Role> userRoles = getUserRoles(loginId);
        roles = userRoles.stream().map(Role::getName).toList();
        // 写缓存
        setRoleListToRedis(loginId, roles);
        log.info("db => loginId: {}, roles: {}", loginId, roles);
        return roles;
    }

    private List<Role> getUserRoles(Object loginId) {
        Integer userId;
        if (loginId instanceof String) {
            userId = Integer.valueOf((String) loginId);
        } else if (loginId instanceof Integer) {
            userId = (Integer) loginId;
        } else {
            throw new IllegalArgumentException("loginId must be String or Integer");
        }
        Assert.notNull(userId, "userId must not be null");
        List<UserRole> userRoles = userRoleService.lambdaQuery().eq(UserRole::getUserId, userId).list();
        if (userRoles == null || userRoles.isEmpty()) {
            return Collections.emptyList();
        }
        List<Role> roles = new ArrayList<>();
        for (UserRole userRole : userRoles) {
            Integer roleId = userRole.getRoleId();
            Role curRole = roleService.getById(roleId);
            roles.add(curRole);
        }
        return roles;
    }

    private List<String> getPermissionListFromRedis(Object loginId) {
        return getListFromRedis(loginId, permissionCachedKey);
    }

    private void setPermissionListToRedis(Object loginId, List<String> permissionList) {
        setListToRedis(loginId, permissionList, permissionCachedKey);
    }


    private List<String> getRoleListFromRedis(Object loginId) {
        return getListFromRedis(loginId, roleCachedKey);
    }

    private void setRoleListToRedis(Object loginId, List<String> roleList) {
        setListToRedis(loginId, roleList, roleCachedKey);
    }

    private List<String> getListFromRedis(Object loginId, String cachedKey) {
        String key = cachedKey.replace("{loginId}", loginId.toString());
        // 如果没有key，没有写过缓存，返回null
        if (Boolean.FALSE.equals(stringRedisTemplate.hasKey(key))) {
            return null;
        }
        return stringRedisTemplate.opsForList().range(key, 0, -1);
    }

    private void setListToRedis(Object loginId, List<String> list, String cachedKey) {
        String key = cachedKey.replace("{loginId}", loginId.toString());
        stringRedisTemplate.opsForList().leftPushAll(key, list);
        stringRedisTemplate.expire(key, 3600, java.util.concurrent.TimeUnit.SECONDS);
    }
}
